﻿using System;

namespace eProject.Model
{
    /// <summary>
    /// This class represents a time period.
    /// </summary>
    public class Period
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="begin">Bbegin of the time period</param>
        /// <param name="end">End of the time period</param>
        /// <exception cref="ArgumentException">If the end is early than the begin </exception>
        public Period(DateTime begin, DateTime end)
        {           
            if (begin >= end) throw new ArgumentException("Begin must be early than End");
            
            _begin = begin;
            _end = end;
            Duration = end.Subtract(begin).TotalDays;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="begin">Begin of the time period</param>
        /// <param name="duration">Duration of the time period(Days)</param>
        public Period(DateTime begin, double duration)
        {
            if (duration <=0) throw  new ArgumentException("Duration must be greater than zero");

            _begin = begin;
            Duration = duration;
            _end = begin.AddDays(duration);            
        }                

        /// <summary>
        /// The begin of the time period
        /// </summary>
        public DateTime Begin { get { return _begin; }}

        /// <summary>
        /// The end of the time period
        /// </summary>
        public DateTime End { get { return _end; } }

        /// <summary>
        /// Time beetwen end and begin (Days).
        /// </summary>        
        public double Duration { get; private set; }

        public Period Shift(double days)
        {
            return new Period(Begin.AddDays(days), Duration);
        }

        public Period ResizeToLeft(double days)
        {
            return new Period(Begin, End.AddDays(days));
        }

        public Period ResizeToRight(double days)
        {
            return new Period(Begin.AddDays(-days), End);//TODO check it
        }

        /// <summary>
        /// Returns period than includes both periods.
        /// For instance: [1,2] and [3,4] = [1,4],
        /// [1,4] and [3,5] = [1,5], etc.
        /// </summary>
        /// <param name="period">Another period</param>
        /// <returns>Union of periods</returns>
        public Period Union(Period period)
        {
            var begin = period.Begin < Begin ? period.Begin : Begin;
            var end = period.End > End ? period.End : End;
            return new Period(begin, end);
        }

        public bool AreIntersect(Period period)
        {
            return period.Begin < End && period.End > Begin;
        }

        public bool Contains(DateTime date)
        {
            return date >= Begin && date < End;
        }

        public bool Equals(Period other)
        {
            if (other == null) return false;            
            if (other == this) return true;

            return Begin.Equals(other.Begin) && End.Equals(other.End);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (Period)) return false;
            return Equals((Period) obj);
        }

        public override int GetHashCode()
        {
            return (Begin.GetHashCode() + "" + End.GetHashCode()).GetHashCode();            
        }

        public override string ToString()
        {
            return "[" + Begin + " , " + End + ")";
        }

        private readonly DateTime _begin;
        private readonly DateTime _end;
    }
}